#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

typedef int MyDataType;

template <typename T>
struct MyData
{
    const char *_name;
    sem_t *_myprodsem;
    sem_t *_myconssem;
    MyData(const char *name, sem_t *myprodsem, sem_t *myconssem)
        : _name(name), _myprodsem(myprodsem), _myconssem(myconssem)
    {
    }
};

void *producer(void *argv)
{
    MyData<MyDataType> *myproducer = static_cast<MyData<MyDataType> *>(argv);
    while (1)
    {
        sem_wait(myproducer->_myprodsem);
        std::cout << myproducer->_name << " send: " << std::endl;
        sem_post(myproducer->_myconssem);
        sleep(1);
    }
}

void *consumer(void *argv)
{
    MyData<MyDataType> *myconsumer = static_cast<MyData<MyDataType> *>(argv);
    while (1)
    {
        sem_wait(myconsumer->_myconssem);
        std::cout << myconsumer->_name << " receive: " << std::endl;
        sem_post(myconsumer->_myprodsem);
        sleep(3);
    }
}

void linux_prodcons()
{
    sem_t *mypprodsem = new sem_t;
    sem_init(mypprodsem, 0, 1);
    sem_t *mypconssem = new sem_t;
    sem_init(mypconssem, 0, 0);

    MyData<MyDataType> *myproducer = new MyData<MyDataType>("producer", mypprodsem, mypconssem);
    MyData<MyDataType> *myconsumer = new MyData<MyDataType>("consumer", mypprodsem, mypconssem);

    pthread_t pth1_id;
    pthread_create(&pth1_id, NULL, producer, (void *)(myproducer));
    pthread_t pth2_id;
    pthread_create(&pth2_id, NULL, consumer, (void *)(myconsumer));

    pthread_join(pth1_id, nullptr);
    pthread_join(pth2_id, nullptr);

    sem_destroy(mypprodsem);
    sem_destroy(mypconssem);

    delete mypprodsem;
    delete mypconssem;
}
